package lockforredis.controller;

import lombok.extern.slf4j.Slf4j;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * redisson锁测试
 *
 * @author: 顾志杰
 */
@RestController
@Slf4j
public class RedissonLockController {

    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedissonClient redissonClient1;
    @Autowired
    private RedissonClient redissonClient2;


    /**
     * 非公平锁 PS：谁抢到锁 谁就能开始业务
     * @return
     */
    @RequestMapping("redissonLockTry")
    public String redissonLockTry(){
        log.info("我进入了方法！");
        /*获取锁*/
        RLock rLock = redissonClient.getLock("lock");
        try {
            /*看门狗机制：如果线程仍旧没有执行完，那么redisson会自动给redis中的目标key延长超时时间，这在Redisson中称之为 Watch Dog 机制。*/
            boolean isLocked = rLock.tryLock(0, 10, TimeUnit.SECONDS);
            if(isLocked){
                try {
                    log.info("我获得了锁");
                    Thread.sleep(10000);
                } finally {
                    log.info("释放锁");
                    rLock.unlock();
                }
            }else{
                log.info("我没抢到锁");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("方法执行完成");
        return "方法执行完成";
    }


    /**
     * 非公平锁 PS：谁抢到锁 谁就能开始业务
     * @return
     */
    @RequestMapping("redissonLock")
    public String redissonLock(){
        log.info("我进入了方法！");
        /*获取锁*/
        RLock rLock = redissonClient.getLock("lock");
        try {
            /*看门狗机制：如果线程仍旧没有执行完，那么redisson会自动给redis中的目标key延长超时时间，这在Redisson中称之为 Watch Dog 机制。*/
            rLock.lock();
            /*设置超时时间  如果业务代码报错或者其他原因  可以释放锁给下一个业务  如果有超时时间则看门狗机制（延时续期功能）作废*/
//            rLock.lock(30, TimeUnit.SECONDS);
            log.info("我获得了锁");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("释放锁");
            rLock.unlock();
        }
        log.info("方法执行完成");
        return "方法执行完成";
    }

    /**
     * 公平锁
     * PS：顺序使用锁 有先来后到
     * @return
     */
    @RequestMapping("redissonFairLock")
    public String redissonFairLock(){
        log.info("我进入了方法！");
        /*获取锁*/
        RLock rLock = redissonClient.getFairLock("lock");
        try {
            /*设置超时时间  如果业务代码报错或者其他原因  可以释放锁给下一个业务*/
            rLock.lock();
            log.info("我获得了锁");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("释放锁");
            rLock.unlock();
        }
        log.info("方法执行完成");
        return "方法执行完成";
    }

    /**
     * 联锁
     * PS：将多个锁关联为一个锁
     * @return
     */
    @RequestMapping("redissonMultiLock")
    public String redissonMultiLock(){
        log.info("我进入了方法！");
        /*可以获取多个实例的redisson锁进行联锁
        PS：必须所有锁都上锁成功后才会生效  如果其中一个redis实例挂掉了导致一个锁不生效则整个联锁都不生效*/
        RLock rLock = redissonClient.getFairLock("lock1");
        RLock rLock1 = redissonClient1.getFairLock("lock2");
        RLock rLock2 = redissonClient2.getFairLock("lock3");
        RedissonMultiLock lock = new RedissonMultiLock(rLock,rLock1,rLock2);
        try {
            lock.lock();
            log.info("我获得了锁");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("释放锁");
            rLock.unlock();
        }
        log.info("方法执行完成");
        return "方法执行完成";
    }

    /**
     * 红锁
     * 有多个redis节点，他们相互独立，都是独立运行在不同的服务器节点里的
     * 通过多个redis实例获取锁保证半数以上获得锁成功就表示当前获得分布式锁是OK的。
     * 假设每个节点消耗1秒，那么初始超时的30秒，减去5秒，剩余25秒，那么此时会在25秒后释放锁，而不是30秒。
     * 如果获得锁失败，则需要对所有节点的锁进行释放，因为我们不知道哪个节点成功哪个节点失败，所以统一对所有的节点进行解锁unlock操作。
     * 如果业务操作成功，则对所有节点释放锁即可。
     * @return
     */
    @RequestMapping("redissonMultiLock")
    public String redissonRedLock(){
        log.info("我进入了方法！");
        RLock rLock = redissonClient.getFairLock("lock1");
        RLock rLock1 = redissonClient1.getFairLock("lock2");
        RLock rLock2 = redissonClient2.getFairLock("lock3");
        RedissonRedLock lock = new RedissonRedLock(rLock,rLock1,rLock2);
        try {
            lock.lock();
            log.info("我获得了锁");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("释放锁");
            rLock.unlock();
        }
        log.info("方法执行完成");
        return "方法执行完成";
    }

    /**
     * 读写锁
     * @return
     */
    @RequestMapping("redissonReadWriteLock")
    public String redissonReadLock() {
        log.info("我进入了方法！");
        RReadWriteLock rLock = redissonClient.getReadWriteLock("lock1");
        /*读锁*/
        rLock.readLock().lock();
        System.out.println("执行业务");
        rLock.readLock().unlock();

        return "方法执行完成";
    }

    /**
     * 读写锁
     * @return
     */
    @RequestMapping("redissonReadWriteLock")
    public String redissonWriteLock() {
        log.info("我进入了方法！");
        RReadWriteLock rLock = redissonClient.getReadWriteLock("lock1");
        /*写锁*/
        rLock.writeLock().lock();
        System.out.println("执行业务");
        rLock.writeLock().unlock();
        return "方法执行完成";
    }

    /**
     * 信号量
     * Redisson的Semaphore本质上和JUC的意思一样，只是可以在分布式下更完善。
     * Semaphore信号量本质上也是用于限流的
     * @param num
     * @throws Exception
     */
    @RequestMapping("testSemaphoreLock")
    public void testSemaphoreLock(Integer num) throws Exception {

        // 定义信号量，声明资源的数量（停车场的车位数，餐厅的饭桌数）
        RSemaphore semaphore = redissonClient.getSemaphore("eat-sema");
        semaphore.trySetPermits(3);

        //如果没有获取到资源则会阻塞
        semaphore.acquire();    // 获得资源数  --1
        System.out.println(num + "号客人" + "来吃饭了~");
        semaphore.release(); //释放资源 ++1
        System.out.println(num + "号客人" + "走人了~");
        //如果没有获取到资源可以降级
//        boolean res = semaphore.tryAcquire();  // 获得资源数  --1
//        if (!res) {
//            GraceException.display();
//        }

    }

    /**
     * 闭锁
     * 设定资源数等所有资源处理完毕才能释放锁
     * @throws Exception
     */
    @RequestMapping("testCountDownLatch")
    public void testCountDownLatch() throws Exception {
        RCountDownLatch cdl = redissonClient.getCountDownLatch("H₂SO₄-Car");
        cdl.trySetCount(3);     // 资源数
        cdl.await();            // 等待全部资源数完成

    }
    @RequestMapping("testDoneStep")
    public void testDoneStep() throws Exception {
        RCountDownLatch cdl = redissonClient.getCountDownLatch("H₂SO₄-Car");
        cdl.countDown();        // 待处理的资源数 ++1

    }
}
